import tensorflow as tf
from tensorflow.keras.datasets import mnist
from tensorflow.keras.models import Sequential
from tensorflow.keras import optimizers, losses, metrics
# Flatten 全连接
from tensorflow.keras.layers import Dense, Dropout, Activation, Flatten
from tensorflow.keras.layers import Conv2D, MaxPooling2D
from tensorflow.keras import utils

#MNIST的CNN案例

# 批处理量
batch_size = 128
nb_classes = 10  #类别数=10
# 循环次数
nb_epoch = 12

# 图片维度设定：高度, 宽度
img_rows, img_cols = 28, 28
# 卷积核数量
nb_filters = 32
# 池化尺寸
pool_size = (2, 2)
# 卷积核尺寸
kernel_size = (3, 3)

# 切分数据集
(X_train, y_train), (X_test, y_test) = mnist.load_data()

#类型转换
X_train = X_train.astype('float32')
X_test = X_test.astype('float32')
# 归一化缩放
X_train /= 255
X_test /= 255
# 将图像转换为: 数量，宽，高，通道
X_train = X_train.reshape(-1, 28, 28, 1)
X_test = X_test.reshape(-1, 28, 28, 1)
print('X_train shape:', X_train.shape)
print(X_train.shape[0], 'train samples')
print(X_test.shape[0], 'test samples')

# 独热编码处理:多分类独热处理
Y_train = utils.to_categorical(y_train, nb_classes)
Y_test = utils.to_categorical(y_test, nb_classes)

#创建模型序列
model = Sequential()
#添加卷积层Conv2D(卷积核数量, 卷积核尺寸, 填充padding(same填充,valid不填充), 输入维度input_shape
model.add(Conv2D(nb_filters, kernel_size, padding='valid', input_shape=(28, 28, 1)))
model.add(Activation('relu')) # 26， 26， 32
model.add(Conv2D(nb_filters, kernel_size)) # 24， 24， 32
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=pool_size))  #最大池化
model.add(Dropout(0.25))  #去掉25%的神经元，防止过拟合

# 全连接处理
model.add(Flatten())  #将卷积展开展平
model.add(Dense(128))  #加入隐藏层128个单元
model.add(Activation('relu'))
model.add(Dropout(0.25))
model.add(Dense(nb_classes))   #输出层10个单元
model.add(Activation('softmax'))

model.summary()
#配置编译模型
model.compile(loss=losses.CategoricalCrossentropy(),  #输入需要独热
              optimizer=optimizers.Adam(0.001),
              metrics=['accuracy'])
#训练模型
model.fit(X_train, Y_train, batch_size=batch_size, epochs=nb_epoch,
          verbose=1, validation_data=(X_test, Y_test))
#模型评估验证
score = model.evaluate(X_test, Y_test, verbose=0)
print('Test score:', score[0]) # 代价
print('Test accuracy:', score[1]) # 准确率
